{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  3.4.3 神经网络参数和TensorFlow变量\n",
    "TF中，变量（tf.Variable）的作用就是保存和更新神经网络中的参数，变量需要指定初始值，共三种初始化方式：\n",
    "- 随机数生成器，tf.random_normal, tf.truncated_normal, tf.random_uniform, tf.random_gamma等，在NN（神经网络）中，随机初始化是最为常见的；\n",
    "<p align='center'>\n",
    "    <img src=images/表3.2.JPG>\n",
    "</p>\n",
    "\n",
    "- 常数，如tf.zeros, tf.ones, tf.fill, tf.constant等，NN中的偏置项(bias)通常会使用常数来初始化，如 `biases = tf.Variable(tf.zeros([3]))`；\n",
    "<p align='center'>\n",
    "    <img src=images/表3.3.JPG>\n",
    "</p>\n",
    "\n",
    "- 其他变量的初始值或其相关运算结果，如 `w2 = tf.Variable(weights.initialized_value() * 2) `。\n",
    "\n",
    "需要注意的是，**变量的初始化分为两部分，上文只是进行了定义，此外还需要在会话中initialize**，这又分为两种方式：\n",
    "- 逐个变量初始化，tf.Variable.initializer方法，变量少时可用，如下面第一个cell；\n",
    "- 一次性初始化所有变量，tf.global_variables_initializer方法，如下面第三个cell。\n",
    "\n",
    "<p align='center'>\n",
    "    <img src=images/图3.6NN前向传播示意图.JPG>\n",
    "</p>\n",
    "\n",
    "上图是神经网络前向传播的示意图，下面一个cell给出了如何通过变量实现上图NN中的参数并实现前向传播的过程："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[3.957578]]\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "# 定义变量及其初始化方法，并设定随机种子保证每次计算结果是一样的。\n",
    "w1= tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))\n",
    "w2= tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))\n",
    "\n",
    "# 暂时将输入的特征定义为一个常量，注意这里是1*2的矩阵。\n",
    "x = tf.constant([[0.7, 0.9]])\n",
    "\n",
    "# 前向传播\n",
    "a = tf.matmul(x, w1)\n",
    "y = tf.matmul(a, w2)\n",
    "\n",
    "# TF程序一般分为两阶段，以上为定义阶段，以下为执行阶段。\n",
    "sess = tf.Session()\n",
    "# 这里不能直接通过sess.run(y)来获取y的值，因为w1和w2的初始化只是定义了，但还没有运行，下面分别运行：\n",
    "sess.run(w1.initializer)  \n",
    "sess.run(w2.initializer)\n",
    "print(sess.run(y)) \n",
    "\n",
    "sess.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**变量和张量的关系**：TF中，变量的声明函数tf.Variable是一个运算，这个运算的输出结果就是一个张量，这个张量也就是本节中介绍的变量，所以变量只是一种特殊的张量，也进一步说明了变量的初始化还需要在Session中initialize。\n",
    "\n",
    "变量的维度和类型是其重要的两个属性，和大部分程序语言类似，**变量的类型是不可变的，但是维度是可变的，不过需要设置参数validate_shape=False，如下：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor 'Assign:0' shape=(2, 2) dtype=float32_ref>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w3 = tf.Variable(tf.random_normal([2, 3], stddev=1), name='w3')\n",
    "w4 = tf.Variable(tf.random_normal([2, 3], stddev=1, dtype=tf.float64), name='w4')\n",
    "w5 = tf.Variable(tf.random_normal([2, 2], stddev=1), name='w5')\n",
    "\n",
    "# tf.assign(w3, w4)  # TypeError，类型不可变\n",
    "# tf.assign(w3, w5)  # ValueError，维度可变，但是需要设置参数，如下：\n",
    "tf.assign(w3, w5, validate_shape=False)\n",
    "# w3.assign(w4, validate_shape=False)  # TypeError，但是tf.Tensor.assign不支持这个参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.4 通过TensorFlow训练神经网络模型\n",
    "上例中，输入为常量，但是神经网络的训练是一个通过很多轮的迭代来找到合适参数的过程，如果每轮迭代中选取的数据都要通过常量来表示，那么TensorFlow的计算图将会太大，而且利用效率低。因为每生成一个常量，TensorFlow都会在计算图中增加一个节点。\n",
    "\n",
    "为了避免这个问题，TensorFlow提供了placeholder机制用于提供输入数据。placeholder相当于定义了一个位置，这个位置中的数据在程序运行时再指定。这样在程序中就不需要生成大量常量来提供输入数据，而只需要将数据通过placeholder传入TensorFlow计算图。\n",
    "\n",
    "在placeholder定义时，这个位置上的数据类型是需要指定的。和其他张量一样，placeholder的类型也是不可以改变的。placeholder中数据的维度信息可以根据提供的数据推导得出，所以不一定要给出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[3.957578]]\n"
     ]
    }
   ],
   "source": [
    "x = tf.placeholder(tf.float32, shape=(1, 2), name=\"input\")\n",
    "a = tf.matmul(x, w1)\n",
    "y = tf.matmul(a, w2)\n",
    "\n",
    "sess = tf.Session()\n",
    "\n",
    "init_op = tf.global_variables_initializer()\n",
    "sess.run(init_op)\n",
    "\n",
    "# print(sess.run(y))  # \n",
    "print(sess.run(y, feed_dict={x: [[0.7,0.9]]}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**在计算前向传播时，需要提供一个feed_dict来指定x的值，feed_dict是一个字典，需要给出每一个用到的placeholder的取值，否则会报错。**\n",
    "\n",
    "在训练NN时，每次需要提供一个batch的训练样例，对于这样的需求，placeholder也可以很好的支持，比如有n个样例，本例中即为x的shape时n*2，下例取n为3，输出矩阵的每一行就代表了每一个样例的前向传播结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[3.957578 ]\n",
      " [1.1537654]\n",
      " [3.1674924]]\n"
     ]
    }
   ],
   "source": [
    "x = tf.placeholder(tf.float32, shape=(3, 2), name=\"input\")\n",
    "a = tf.matmul(x, w1)\n",
    "y = tf.matmul(a, w2)\n",
    "\n",
    "sess = tf.Session()\n",
    "#使用tf.global_variables_initializer()来初始化所有的变量\n",
    "init_op = tf.global_variables_initializer()  \n",
    "sess.run(init_op)\n",
    "\n",
    "print(sess.run(y, feed_dict={x: [[0.7,0.9],[0.1,0.4],[0.5,0.8]]})) "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
